﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BOMTest
{
    class Program
    {
        static void Main(string[] args)
        {
            #region 常见编码
            //string content = "abc我是谁666";

            //Console.WriteLine($"明文:{content}");

            //Console.WriteLine();
            //Console.WriteLine("ASCII...");
            //byte[] ASCIIbytes = Encoding.ASCII.GetBytes(content);
            //PrintBytes(ASCIIbytes);
            //PrintBinary(ASCIIbytes);

            //Console.WriteLine();
            //Console.WriteLine("UTF8...");
            //byte[] UTF8bytes = Encoding.UTF8.GetBytes(content);
            //PrintBytes(UTF8bytes);
            //PrintBinary(UTF8bytes);

            //Console.WriteLine();
            //Console.WriteLine("UTF16 小端序...");
            //byte[] UTF16bytes = Encoding.Unicode.GetBytes(content);
            //PrintBytes(UTF16bytes);
            //PrintBinary(UTF16bytes);

            //Console.WriteLine();
            //Console.WriteLine("UTF16 大端序...");
            //byte[] UTF16Bigbytes = Encoding.BigEndianUnicode.GetBytes(content);
            //PrintBytes(UTF16Bigbytes);
            //PrintBinary(UTF16Bigbytes);

            //Console.WriteLine();
            //Console.WriteLine("UTF32...");
            //byte[] UTF32bytes = Encoding.UTF32.GetBytes(content);
            //PrintBytes(UTF32bytes);
            //PrintBinary(UTF32bytes);
            #endregion

            #region 常见编码BOM
            // 通过GetPreamble()方法获取编码BOM
            //Console.WriteLine("UTF8...");
            //PrintBytes(Encoding.UTF8.GetPreamble());

            //Console.WriteLine();
            //Console.WriteLine("UTF16 小端序");
            //PrintBytes(Encoding.Unicode.GetPreamble());

            //Console.WriteLine();
            //Console.WriteLine("UTF16 大端序...");
            //PrintBytes(Encoding.BigEndianUnicode.GetPreamble());

            //Console.WriteLine();
            //Console.WriteLine("UTF32...");
            //PrintBytes(Encoding.UTF32.GetPreamble());
            #endregion

            #region 文件写和BOM
            // 文本写入，StreamWriter类和File.WriteAllText方法的默认编码都是不带BOM的UTF8, 可以通过构造函数指定
            //var path1 = Path.GetTempFileName();
            //var path2 = Path.GetTempFileName();
            //var path3 = Path.GetTempFileName();

            //string content = "abcde";

            //Console.WriteLine("默认无BOM的UTF8...");
            //File.WriteAllText(path1, content);
            //PrintBytes(File.ReadAllBytes(path1));

            //Console.WriteLine();
            //Encoding utf8WithBom = Encoding.UTF8;
            //Console.WriteLine("指定编码，默认带BOM的UTF8...");
            //File.WriteAllText(path1, content, utf8WithBom);
            //PrintBytes(File.ReadAllBytes(path1));

            //Console.WriteLine();
            //Encoding utf8NoBom = new UTF8Encoding(false, false);
            //Console.WriteLine("指定编码，通过构造函数去除BOM的UTF8...");
            //File.WriteAllText(path1, content, utf8NoBom);
            //PrintBytes(File.ReadAllBytes(path1));
            #endregion

            #region 文件读和BOM
            // 文件读取，StringReader和File.ReadAllText都会自动识别BOM来判定字符编码
            // File.ReadAllText默认编码是UTF-8
            // File.ReadAllText是调用了StreamReader构造函数
            // public StreamReader(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize)
            // 当detectEncodingFromByteOrderMarks参数是true时，StreamReader还是会自动识别BOM来读文件的

            //var path1 = Path.GetTempFileName();
            //var path2 = Path.GetTempFileName();

            //string content = "abc我是谁";
            //File.WriteAllText(path1, content);                  //使用默认没有BOM的UTF8编码写文件
            //File.WriteAllText(path2, content, Encoding.UTF8);   //使用带BOM的UTF8编码

            //PrintBytes(File.ReadAllBytes(path1));
            //PrintBytes(File.ReadAllBytes(path2));

            //Console.WriteLine(File.ReadAllText(path1));
            //Console.WriteLine(File.ReadAllText(path2));
            #endregion

            #region 文件读和BOM（错误情况）
            var path1 = Path.GetTempFileName();
            var path2 = Path.GetTempFileName();

            string content = "abc我是谁";

            File.WriteAllText(path1, content, Encoding.Unicode);                  //使用带BOM的UTF16编码
            File.WriteAllText(path2, content, new UnicodeEncoding(false, false)); //使用没有BOM的UTF16编码写文件

            PrintBytes(File.ReadAllBytes(path1));
            PrintBytes(File.ReadAllBytes(path2));

            string c1 = File.ReadAllText(path1);                    //自动觉察BOM读文件           
            string c2 = File.ReadAllText(path2);                    //path2没BOM，实际上用缺省UTF8读文件            
            string c3 = File.ReadAllText(path2, Encoding.Unicode);  //path2没BOM，用正确度UTF16读文件

            ShowContent(c1);
            ShowContent(c2);
            ShowContent(c3);
            #endregion

            #region 去掉BOM
            //var path = Path.GetTempFileName();
            //File.WriteAllText(path, "abc我是谁", Encoding.UTF8);

            //PrintBytes(File.ReadAllBytes(path));
            //PrintBytes(GetBytesWithoutBOM(path, Encoding.UTF8));

            //using (Stream stream = File.OpenRead(path))
            //{
            //    SkipBOM(stream, Encoding.UTF8);
            //    int data;
            //    while ((data = stream.ReadByte()) != -1)
            //        Console.Write("{0:X2} ", data);
            //    Console.WriteLine();
            //}
            #endregion

            Console.ReadKey();
        }

        static byte[] GetBytesWithoutBOM(string path, Encoding encoding)
        {
            return File.ReadAllBytes(path).Skip(encoding.GetPreamble().Length).ToArray();
        }

        static void SkipBOM(Stream stream, Encoding enc)
        {
            stream.Seek(enc.GetPreamble().Length, SeekOrigin.Begin);
        }

        static void ShowContent(string content)
        {
            Console.WriteLine("读入字符数：{0}  内容：{1}", content.Length, content);
        }

        static void WriteAndPrint(string content, Encoding encoding)
        {
            var path = Path.GetTempFileName();
            File.WriteAllText(path, content, encoding);
            PrintBytes(File.ReadAllBytes(path));
        }

        static void PrintBytes(byte[] bytes)
        {
            if (bytes == null || bytes.Length == 0)
            {
                Console.WriteLine("<无值>");
            }

            foreach (var b in bytes)
            {
                Console.Write("{0:X2} ", b);
            }

            Console.WriteLine();
        }

        static void PrintBinary(byte[] bytes)
        {
            if (bytes == null || bytes.Length == 0)
            {
                Console.WriteLine("<无值>");
            }                

            foreach (var b in bytes)
            {
                Console.Write(Convert.ToString(b, 2).PadLeft(8, '0') + " ");
            }

            Console.WriteLine();
        }
    }
}
